<?php
/**
 * Copyright (c) 2010 Nicolas Crovatti, http://shinylittlething.com 
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
*/
/**
 * @package		cssShrink
 * @author			Nicolas Crovatti <nico@shinylittlething.com>
 * @copyright		Copyright (c) 2010 Nicolas Crovatti
 * @version		$Revision$
 * @license		http://en.wikipedia.org/wiki/MIT_License
 * @link			http://shinylittlething.com/2010/01/20/css-minification-on-the-fly/#comments
*/

class cssShrink {
	const 
		VERSION		= '0.3';
		
	private 
		$_start 	= null,
		$_end 		= null,
		$_lifetime 	= 86400,
		$_css_file 	= array();
		
	public 
		$cache_dir	= './cache/',
		$runtime	= 0,
		$cache_file	= null;
		
	public function __construct($css_uri) {
		if (!isset($css_uri)) {
			return false;
		}
		
		$this->css_file['name'] = $_SERVER['DOCUMENT_ROOT'] . $css_uri;
		
		$this->setup();
		
		if ($this->cache_valid()) {
			$this->display_cache();
		} 
		else {
			$this->build_then_display_cache();	
		}
		
		exit;
	}
	
	public function display_cache() {
		$this->_stop();
		header("Cache-Status: cached");
		header("X-Runtime: ". $this->runtime . "sec");
		include($this->cache_file['name']);		
	}
	
	
	public function build_then_display_cache() {
		$this->_stop();
		
		header("Cache-Status: regenerated"); 
		$fp 		= fopen($this->cache_file['name'], 'w'); 
		$content 	= file_get_contents($this->css_file['name']);

		$cache_info =  "/* Cached ". date('jS F Y H:i', $this->cache_file['age'])." */";
		$data = $this->minify($content) . $cache_info ;
		fwrite($fp, $data);
		fclose($fp);

		echo $data;
	}
	
	
	public function cache_valid() {

		if ( filesize($this->cache_file['name']) > 0
			&& time() - $this->_lifetime < $this->cache_file['age'] 
			&& $this->css_file['age'] < $this->cache_file['age']) {	
			return true;
		}
		
		return false;
		
	}
	
	public function setup() {
		$this->_start();
		
 		$this->css_file['age']		= filemtime($this->css_file['name']);

		$this->cache_file['name'] 	= $this->cache_dir . 
										basename($this->css_file['name']) . '-' . 
										md5($this->css_file['name']);
		if(!file_exists($this->cache_file['name'])) {
			touch($this->cache_file['name']);
		}
		
		$this->cache_file['age'] 	= filemtime($this->cache_file['name']);
		
		$this->_expires 			= gmdate( "D, d M Y H:i:s", $this->cache_file['age'] + $this->_lifetime);
		
		header("Content-type: text/css");
		header("X-Powered-by: CSS Minifier v" . self::VERSION, true);		
		header("X-Original-generated: " . $this->_nicetime($this->css_file['age']));
		header("Expires: " . $this->_expires . " GMT", true);
		
	}
	
	private function _start() {
    	list($usec, $sec) = explode(" ", microtime());
		$this->_start = (float)$usec + (float)$sec;
	}
	
	private function _stop() {
    	list($usec, $sec) = explode(" ", microtime());
		$this->_end = (float)$usec + (float)$sec;
		$this->runtime = $this->_end - $this->_start;
	}
	
	private function minify($data) {
		$replacements = array(
			'#/\*.*?\*/#s'	=> '',
			'/(\t|\r|\n)/' 	=> '',
			'/[^}{]+{\s?}/' => '',
			'/\s*{\s*/' 	=> '{',
			'/;}/' 			=> '}',
			'/, /' 			=> ',',
			'/; /' 			=> ';',
			'/: /' 			=> ':',
			'#\s+#' 		=> ' ',
		);
		
		foreach($replacements as $from => $to) {
			$data = preg_replace($from, $to, $data );
		}
   
    	return $data;
	}

	
		
	private function _nicetime($unix_date) {
	    if(empty($unix_date)) {
	        return false;
	    }
   
	    $periods	= array(
			"second", "minute", "hour", 
			"day", "week", "month", "year", 
			"decade"
		);
		
	    $lengths	= array(
			"60","60","24","7",
			"4.35","12","10"
		);
   
	    $now		= time();

	    if($now > $unix_date) {   
	        $difference	= $now - $unix_date;
	        $tense		= "ago";
	    } 
		else {
	        $difference	= $unix_date - $now;
	        $tense		= "from now";
	    }
   
	    for($j = 0; $difference >= $lengths[$j] 
			&& $j < count($lengths)-1; $j++) {
	        $difference /= $lengths[$j];
	    }
   
	    $difference = round($difference);
   
	    if($difference != 1) {
	        $periods[$j] .= "s";
	    }
   
	    return $difference .' '. $periods[$j] .' '. $tense;
	}
}

$minifier = new cssShrink($_GET['css']);
